import static org.junit.Assert.*;
import junit.framework.TestCase;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

/**
 * 
 */

/**
 * @author DADEV
 *
 */
public class VendingMachineTest extends TestCase{

	private VendingMachine vm = null;
	private VendingMachineItem vmi1 = null;
	private VendingMachineItem vmi2 = null;
	private VendingMachineItem vmi3 = null;
	private VendingMachineItem vmi4 = null;
	private VendingMachineItem vmi5 = null;
	/**
	 * @throws java.lang.Exception
	 * creates default vending machine and items to be used in tests
	 */
	@Before
	public void setUp() throws Exception {
		vm = new VendingMachine();
		vmi1 = new VendingMachineItem("item1", 1.5);
		vmi2 = new VendingMachineItem("item2", 2.5);
		vmi3 = new VendingMachineItem("item3", 3.5);
		vmi4 = new VendingMachineItem("item4", 4.5);
		vmi5 = new VendingMachineItem("item4", 0.0);
	}

	/**
	 * @throws java.lang.Exception
	 */
	@After
	public void tearDown() throws Exception {
		vm = null;
		vmi1 = null;
		vmi2 = null;
		vmi3 = null;
		vmi4 = null;
		vmi5 = null;
	}

	/**
	 * Test method for {@link VendingMachine#addItem(VendingMachineItem, java.lang.String)}.
	 * Adds items to each slot in the vending machine then determines that the item is the 
	 * same as the item we just added
	 */
	@Test
	public void testAddItem()
	throws VendingMachineException {
		vm.addItem(vmi5, "A");
		assertTrue(vm.getItem("A")==vmi5);
		vm.addItem(vmi2, "B");
		assertTrue(vm.getItem("B")==vmi2);
		vm.addItem(vmi3, "C");
		assertTrue(vm.getItem("C")==vmi3);
		vm.addItem(vmi4, "D");
		assertTrue(vm.getItem("D")==vmi4);
	}
	/**
	 * Test method for {@link VendingMachine#addItem(VendingMachineItem, java.lang.String)}.
	 * Adds items to each slot in the vending machine then determines that the item is the 
	 * same as the item we just added and that an invalid slot is not allowed to be added to
	 */
	@Test(expected=VendingMachineException.class)
	public void testAddItemInvalidCode() {
		try{
			vm.addItem(vmi4, "E");
		}
		catch(VendingMachineException e)
		{
			assertEquals(e.getClass(),VendingMachineException.class);
		}
	}
	/**
	 * Test method for {@link VendingMachine#addItem(VendingMachineItem, java.lang.String)}.
	 * Adds items to each slot in the vending machine then adds a second item to the same slot
	 * and then determines that the item throws an error
	 */
	@Test (expected=VendingMachineException.class) 
	public void testAddItemDouble() {
		try{
			
			vm.addItem(vmi1, "A");
			vm.addItem(vmi2, "A");
		}
		catch (VendingMachineException e)
		{
			assertEquals(e.getMessage(),"Slot A already occupied");
		}
	}

	/**
	 * Test method for {@link VendingMachine#removeItem(java.lang.String)}.
	 * Adds and item to the vending machine in a slot then removes it from that slot
	 * and then makes sure that when we get the item we just removed, a null is returned.
	 */
	@Test
	public void testRemoveItem() {
		vm.addItem(vmi1, "A");
		vm.removeItem("A");
		assertNull(vm.getItem("A"));
		vm.addItem(vmi2, "B");
		vm.removeItem("B");
		assertNull(vm.getItem("B"));
		vm.addItem(vmi3, "C");
		vm.removeItem("C");
		assertNull(vm.getItem("C"));
		vm.addItem(vmi4, "D");
		vm.removeItem("D");
		assertNull(vm.getItem("D"));
	}

	/**
	 * Test method for {@link VendingMachine#removeItem(java.lang.String)}.
	 * Adds and item to the vending machine in a slot then removes an item from a slot with
	 * no valid entries in it.
	 */
	@Test(expected=VendingMachineException.class)
	public void testRemoveItemInvalid() {
		try
		{
		vm.removeItem("B");
		}
		catch(VendingMachineException e)
		{
			assertEquals(e.getClass(),VendingMachineException.class);
		}
	}
	/**
	 * Test method for {@link VendingMachine#removeItem(java.lang.String)}.
	 * Adds and item to the vending machine in a slot then removes an item from a slot with
	 * no valid entries in it.
	 */
	@Test(expected=VendingMachineException.class)
	public void testRemoveItemInvalidCode() {
		try
		{
		vm.removeItem("E");
		}
		catch(VendingMachineException e)
		{
			assertEquals(e.getClass(),VendingMachineException.class);
		}
	}
	/**
	 * Test method for {@link VendingMachine#insertMoney(double)}.
	 * makes sure the money inserted is the same as the one we just added
	 */
	@Test
	public void testInsertMoney() {
		vm.addItem(vmi1, "A");
		vm.insertMoney(1.75);
		assertEquals(vm.balance,1.75);
	}
	/**
	 * Test method for {@link VendingMachine#insertMoney(double)}.
	 * makes sure the money inserted is the same as the one we just added
	 */
	@Test(expected=VendingMachineException.class)
	public void testInsertMoneyInvalid() {
		try
		{
			vm.insertMoney(-1.75);
		}
		catch (VendingMachineException e)
		{
			assertEquals(e.getClass(),VendingMachineException.class);
			assertEquals(e.getMessage(),"Invalid amount.  Amount must be >= 0");
		}
	}


	/**
	 * Test method for {@link VendingMachine#getBalance()}.
	 * makes sure the balance is the same as the total of the money added after several inserts
	 */
	@Test
	public void testGetBalance()
	throws VendingMachineException 
	{
		vm.addItem(vmi1, "A");
		vm.insertMoney(1.25);
		vm.insertMoney(1.0);
		vm.insertMoney(0.25);
		assertEquals(vm.getBalance(),2.50);
	}

	/**
	 * Test method for {@link VendingMachine#makePurchase(java.lang.String)}.
	 * makes a purchase from the vending machine after inserting more money then required
	 */
	@Test
	public void testMakePurchase() {
		vm.addItem(vmi1, "A");
		vm.insertMoney(1.25);
		vm.insertMoney(1.0);
		vm.insertMoney(0.25);
		assertTrue(vm.makePurchase("A"));
	}
	/**
	 * Test method for {@link VendingMachine#makePurchase(java.lang.String)}.
	 * makes a purchase from the vending machine after inserting more money then required for 
	 * an item which is free
	 */
	@Test
	public void testMakePurchaseFreeItem() 
	throws VendingMachineException {
		vm.insertMoney(1.25);
		vm.insertMoney(1.0);
		vm.insertMoney(0.25);
		vm.addItem(vmi5, "B");
		assertTrue(vm.makePurchase("B"));
	}
	


	/**
	 * Test method for {@link VendingMachine#returnChange()}.
	 * returns the change for a purchase after inserting more money then required
	 */
	@Test
	public void testReturnChange() {
		vm.addItem(vmi1, "A");
		vm.insertMoney(1.25);
		vm.insertMoney(1.0);
		vm.insertMoney(0.25);
		vm.makePurchase("A");
		assertTrue(vm.returnChange()==1.0);
	}

}
